﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace Apewer.Internals
{

    internal static class AccessHelper
    {

        [DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
        static extern void RtlMoveMemory(ref double des, int src, int count);

        internal const string JetOleDB4 = "microsoft.jet.oledb.4.0";
        internal const string AceOleDB12 = "microsoft.ace.oledb.12.0";

        #region 获取密码

        public static string[] GetPassword(string path)
        {
            var list = new List<string>();
            if (File.Exists(path))
            {
                FileStream file = null;
                try
                {
                    file = File.OpenRead(path);
                    GetPassword(list, file, GetPassword_1);
                    GetPassword(list, file, GetPassword_2);
                }
                catch { }
                if (file != null)
                {
                    file.Close();
                    file.Dispose();
                }
            }
            return list.ToArray();
        }

        static void GetPassword(List<string> list, FileStream file, Func<FileStream, string> func)
        {
            file.Position = 0L;
            file.Seek(0L, SeekOrigin.Begin);
            try
            {
                var password = func(file);

                if (string.IsNullOrEmpty(password)) return;
                if (list.Contains(password)) return;
                list.Add(password);
            }
            catch { }
        }

        #region 方式 1

        static unsafe string GetPassword_1(FileStream file)
        {
            var password = "";

            // 检查格式版本标记，0 为 Access 97，1 为 Accessor 2000。
            file.Seek(0x14, SeekOrigin.Begin);
            var verFlag = (byte)file.ReadByte();

            // 检查文件类型标记，不含有标记的文件无法识别。
            file.Seek(0x4, SeekOrigin.Begin);
            var bs = new byte[15];
            file.Read(bs, 0, 15);
            var FileFlag = Encoding.Default.GetString(bs);
            if (FileFlag != "Standard Jet DB") return null;

            // 读取加密区。
            file.Seek(0x18, SeekOrigin.Begin);
            var originBytes = new byte[128];
            file.Read(originBytes, 0, 128);


            var encryptedKey = new byte[258];
            var dbl = 0D;
            encryptedKey = GetEncryptedString();
            originBytes = GetKey(encryptedKey, originBytes);
            double* dblAdress = &dbl;
            fixed (byte* bskeysAdress = &originBytes[0]) RtlMoveMemory(ref *dblAdress, (int)bskeysAdress + 90, 8);

            var passData = new long[19];
            for (int j = 0; j < 19; j++)
            {
                passData[j] = (long)originBytes[j * 2 + 42] + 256 * (long)(originBytes[j * 2 + 43]);
                if (j % 2 == 0) passData[j] = passData[j] ^ (int)dbl; // dbl = 42416;
                if (passData[j] != 0) password += (char)passData[j];
            }

            return password;
        }

        static byte[] GetEncryptedString()
        {
            long temp1 = 0;
            long temp2 = 0;
            long temp3 = 0;
            long temp4 = 0;
            long temp5 = 0;
            byte[] encryptedRet = new byte[258];
            byte[] encriptedKey = new byte[4];
            long temp = 0x800000FF;
            encriptedKey[0] = 0xC7;
            encriptedKey[1] = 0xDA;
            encriptedKey[2] = 0x39;
            encriptedKey[3] = 0x6B;

            for (int i = 0; i < 256; i++)
            {
                encryptedRet[i] = (byte)i;
            }
            for (int i = 0; i < 256; i++)
            {
                temp1 = temp2;
                temp1 = encriptedKey[temp1];
                temp4 = encryptedRet[i];
                temp1 = temp1 + temp4;
                temp4 = temp3;
                temp1 = temp1 + temp4;
                temp1 = (byte)((byte)temp1 & (byte)(temp));
                temp3 = temp1;
                temp1 = encryptedRet[i];
                temp5 = temp1;
                temp1 = temp3;
                temp1 = encryptedRet[temp1];
                encryptedRet[i] = (byte)temp1;
                temp4 = temp3;
                encryptedRet[temp4] = (byte)temp5;
                temp1 = temp2;
                temp1 = temp1 + 1;
                temp4 = temp1 % 4;
                temp2 = temp4;
            }
            return encryptedRet;
        }

        static byte[] GetKey(byte[] encryptedKey, byte[] originBytes)
        {
            long temp1 = 0;
            long temp2 = 0;
            long temp3 = 0;
            long temp4 = 0;
            long temp5 = 0;
            long temp6 = 0;
            long temp7 = 0;
            long temp8 = 0;
            temp4 = encryptedKey[0x100];
            temp1 = encryptedKey[0x101];
            long temp = 0x800000FF;
            temp1 = 0;
            for (int i = 1; i < 129; i++)
            {
                temp4 = temp4 + 1;
                temp4 = (byte)((byte)temp4 & (byte)(temp));
                temp3 = (byte)((byte)temp4 & (byte)(0xFF));
                temp5 = encryptedKey[temp3];
                temp1 = (byte)((byte)temp1 & (byte)(0xFF));
                temp5 = temp5 + temp1;
                temp1 = (byte)((byte)temp5 & (byte)(temp));
                temp6 = encryptedKey[temp4];
                temp5 = encryptedKey[temp1];
                encryptedKey[temp3] = (byte)temp5;
                temp2 = temp1;
                encryptedKey[temp2] = (byte)temp6;
                temp5 = encryptedKey[temp3];
                temp3 = encryptedKey[(byte)((byte)temp1 & (byte)(0xFF))];
                temp5 = temp5 + temp3;
                temp5 = (byte)((byte)temp5 & (byte)(temp)); ;
                temp7 = temp5;
                temp3 = temp8;
                temp5 = encryptedKey[temp5];
                originBytes[temp3] = (byte)(originBytes[temp3] ^ (byte)temp5);
                temp8 = temp8 + 1;
            }
            encryptedKey[0x100] = (byte)temp4;
            encryptedKey[0x101] = (byte)temp1;
            return originBytes;
        }

        #endregion

        #region 方式 2

        static string GetPassword_2(FileStream file)
        {
            var password = "";

            // 未加密的文件 0x42 开始至 0x61 之前的每间隔一字节的数值。
            var baseByte = new byte[] { 0xbe, 0xec, 0x65, 0x9c, 0xfe, 0x28, 0x2b, 0x8a, 0x6c, 0x7b, 0xcd, 0xdf, 0x4f, 0x13, 0xf7, 0xb1, };

            // 标志 0x62 处的数值。
            byte flagByte = 0x0c;

            // 检查格式版本标记，0 为 Access 97，1 为 Accessor 2000。
            file.Seek(0x14, SeekOrigin.Begin);
            byte verFlag = (byte)file.ReadByte();

            file.Seek(0x42, SeekOrigin.Begin);
            byte[] bs = new byte[33];
            if (file.Read(bs, 0, 33) != 33) return "";
            byte flag = (byte)(bs[32] ^ flagByte);
            for (int i = 0; i < 16; i++)
            {
                byte b = (byte)(baseByte[i] ^ (bs[i * 2]));

                // Access 2000
                if (i % 2 == 0 && verFlag == 1) b ^= flag;
                if (b > 0)
                {
                    var ch = (char)b;
                    password += ch;
                }
            }

            return password;
        }

        #endregion

        #endregion

    }

}
